package com.dcivision.dms.core;

import java.sql.Connection;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.impl.LogFactoryImpl;

import com.dcivision.dms.bean.DmsRoot;
import com.dcivision.dms.bean.DmsTreeNode;
import com.dcivision.dms.dao.DmsPermissionDAObject;
import com.dcivision.framework.GlobalConstant;
import com.dcivision.framework.PermissionManager;
import com.dcivision.framework.SessionContainer;
import com.dcivision.user.bean.UserGroup;
import com.dcivision.user.bean.UserRole;

public class DmsTreeController {
  
  private Log log = new LogFactoryImpl().getInstance(this.getClass());
  
  private final String OBJECT_TYPE = GlobalConstant.OBJECT_TYPE_DOCUMENT;
  
  private final String FILTER_RIGHT = "R";
  
  private SessionContainer sessionContainer = null;
  
  private Connection dbConn = null;
  
  protected PermissionManager permissionManager = null;
          
  public DmsTreeController() {
  }
  
  public DmsTreeController(SessionContainer sessionContainer, Connection dbConn) {
    this();
    this.sessionContainer = sessionContainer;
    this.dbConn = dbConn;
    this.permissionManager = sessionContainer.getPermissionManager();
  }
  
  public void initPermissionOnlyByParameters(String rootType, Integer rootId, Integer ownerId, Integer parentId) throws Exception {
    DmsPermissionDAObject dmsTreeDAObject = new DmsPermissionDAObject(this.sessionContainer, this.dbConn);
    List parentIds = this.getTreeNodeParentIds(dmsTreeDAObject, parentId);
    for (int i=(parentIds.size()-1); i>=0; i--) {
      Integer pid = (Integer)parentIds.get(i);
      List lDmsTreeNodes = dmsTreeDAObject.getDmsTreeNodeByParent(pid, rootId);
      permissionManager.appendPermissionCache(this.dbConn, this.getTreeNodeIds(lDmsTreeNodes), this.OBJECT_TYPE, rootType, ownerId);
      //this.searchThenAppendPermissionCache(dmsTreeDAObject, lDmsTreeNodes, rootType, ownerId);
    }
    List lDmsTreeNodes = dmsTreeDAObject.getDmsTreeNodeByParent(parentId, rootId);
    permissionManager.appendPermissionCache(this.dbConn, this.getTreeNodeIds(lDmsTreeNodes), this.OBJECT_TYPE, rootType, ownerId);
    //this.searchThenAppendPermissionCache(dmsTreeDAObject, lDmsTreeNodes, rootType, ownerId);
  }
  
  public String getDmsTreeNodeStringInfoByParameters(String rootType, Integer rootId, Integer ownerId, Integer parentId) throws Exception {
    return this.getDmsTreeNodeStringInfoByParameters(rootType, rootId, ownerId, parentId, "true");
  }
  
  public String getDmsTreeNodeStringInfoByParameters(String rootType, Integer rootId, Integer ownerId, Integer parentId, String usePermission) throws Exception {
    StringBuffer sbTreeInfo = new StringBuffer();
    List dmsTreeNodeList = this.getDmsTreeNodeListByParameter(rootType, rootId, ownerId, parentId, usePermission);
    for (int i=0; i<dmsTreeNodeList.size(); i++) {
      DmsTreeNode node = (DmsTreeNode)dmsTreeNodeList.get(i);
      Integer id = node.getId();
      Integer pid = node.getParentId();
      String  name = node.getDocumentName();
      String type = node.getDocumentType();
      boolean isHasChildren = node.isHasChildren();
      boolean isLastNode = ((i+1)==dmsTreeNodeList.size());
      
      sbTreeInfo.append((i==0 ? "" : ":#:"));
      sbTreeInfo.append(id+"|#|"+pid+"|#|"+name+"|#|"+type+"|#|"+isHasChildren+"|#|"+isLastNode);
    }
    return sbTreeInfo.toString();
  }
  
  public DmsTreeNode getDmsTreeNode(String rootType, Integer rootId, Integer ownerId, Integer Id) throws Exception {
    return this.getDmsTreeNode(rootType, rootId, ownerId, Id, "true");
  }
  
  public DmsTreeNode getDmsTreeNode(String rootType, Integer rootId, Integer ownerId, Integer Id, String usePermission) throws Exception {
    DmsPermissionDAObject dmsTreeDAObject = new DmsPermissionDAObject(this.sessionContainer, this.dbConn);
    List ids = new ArrayList(); ids.add(Id);
    List lDmsTreeNodes = dmsTreeDAObject.getDmsTreeNodeByIds(ids, rootId);
    if (!lDmsTreeNodes.isEmpty()) {
      if (this.sessionContainer.getByPassPermission() || usePermission.equals("false")) {
        List lDmsChildrenTreeNodes = dmsTreeDAObject.getDmsTreeNodeByParents(this.getTreeNodeIds(lDmsTreeNodes), rootId);
        lDmsTreeNodes = this.getTreeNodesAfterSetHasChildren(lDmsTreeNodes, lDmsChildrenTreeNodes);
      }else {
        permissionManager.appendPermissionCache(this.dbConn, this.getTreeNodeIds(lDmsTreeNodes), this.OBJECT_TYPE, rootType, ownerId);
        //this.searchThenAppendPermissionCache(dmsTreeDAObject, lDmsTreeNodes, rootType, ownerId);
        List lDmsChildrenTreeNodes = dmsTreeDAObject.getDmsTreeNodeByParents(this.getTreeNodeIds(lDmsTreeNodes), rootId);
        if (!lDmsChildrenTreeNodes.isEmpty()) {
          permissionManager.appendPermissionCache(this.dbConn, this.getTreeNodeIds(lDmsChildrenTreeNodes), this.OBJECT_TYPE, rootType, ownerId);
          //this.searchThenAppendPermissionCache(dmsTreeDAObject, lDmsChildrenTreeNodes, rootType, ownerId);
        }
        lDmsTreeNodes = this.getFilterTreeNodes(lDmsTreeNodes);
        lDmsChildrenTreeNodes = this.getFilterTreeNodes(lDmsChildrenTreeNodes);
        lDmsTreeNodes = this.getTreeNodesAfterSetHasChildren(lDmsTreeNodes, lDmsChildrenTreeNodes);
      }
    }
    return (lDmsTreeNodes.isEmpty() ? null : (DmsTreeNode)lDmsTreeNodes.get(0));
  }
  
  public List getDmsTreeNodeListByParameter(String rootType, Integer rootId, Integer ownerId, Integer parentId) throws Exception {
    return this.getDmsTreeNodeListByParameter(rootType, rootId, ownerId, parentId, "true");
  }
  
  public List getDmsTreeNodeListByParameter(String rootType, Integer rootId, Integer ownerId, Integer parentId, String usePermission) throws Exception {
    List dmsTreeNodeList = null;
    try {
      DmsPermissionDAObject dmsTreeDAObject = new DmsPermissionDAObject(this.sessionContainer, this.dbConn);
      List lDmsTreeNodes = dmsTreeDAObject.getDmsTreeNodeByParent(parentId, rootId);
      
      if (this.sessionContainer.getByPassPermission() || usePermission.equals("false")) {
        List lDmsChildrenTreeNodes = dmsTreeDAObject.getDmsTreeNodeByParents(this.getTreeNodeIds(lDmsTreeNodes), rootId);
        dmsTreeNodeList = this.getTreeNodesAfterSetHasChildren(lDmsTreeNodes, lDmsChildrenTreeNodes);
        return dmsTreeNodeList;
      }else {
        if (!lDmsTreeNodes.isEmpty()) {
          permissionManager.appendPermissionCache(this.dbConn, this.getTreeNodeIds(lDmsTreeNodes), this.OBJECT_TYPE, rootType, ownerId);
          //this.searchThenAppendPermissionCache(dmsTreeDAObject, lDmsTreeNodes, rootType, ownerId);
          
          List lDmsChildrenTreeNodes = dmsTreeDAObject.getDmsTreeNodeByParents(this.getTreeNodeIds(lDmsTreeNodes), rootId);
          if (!lDmsChildrenTreeNodes.isEmpty()) {
            permissionManager.appendPermissionCache(this.dbConn, this.getTreeNodeIds(lDmsChildrenTreeNodes), this.OBJECT_TYPE, rootType, ownerId);
            //this.searchThenAppendPermissionCache(dmsTreeDAObject, lDmsChildrenTreeNodes, rootType, ownerId);
          }
  
          lDmsTreeNodes = this.getFilterTreeNodes(lDmsTreeNodes);
          lDmsChildrenTreeNodes = this.getFilterTreeNodes(lDmsChildrenTreeNodes);
          
          dmsTreeNodeList = this.getTreeNodesAfterSetHasChildren(lDmsTreeNodes, lDmsChildrenTreeNodes);
          return dmsTreeNodeList;
        }else {
          return new ArrayList();
        }
      }
    }catch (Exception ex) {
      throw ex;
    }
  }
  
  private List getTreeNodeParentIds(DmsPermissionDAObject dmsTreeDAObject, Integer id) throws Exception {
    List lParentIds = new ArrayList();
    Integer pid = dmsTreeDAObject.getDocumentParentId(id);
    if (pid!=null && pid.intValue()>0) {
      lParentIds.add(pid);
      lParentIds.addAll(this.getTreeNodeParentIds(dmsTreeDAObject, pid));
    }
    return lParentIds;
    
  }
  
  /**
   * This method is deprecated, and replace it with permissionManager.appendPermissionCache() method. 
   * lee 06/12/14 EIP-777
   * @param dmsTreeDAObject
   * @param lTreeNodes
   * @param rootType
   * @param ownerId
   * @throws Exception
   * @deprecated This method is deprecated.
   */
  private void searchThenAppendPermissionCache(
      DmsPermissionDAObject dmsTreeDAObject, 
      List lTreeNodes,
      String rootType,
      Integer ownerId ) throws Exception {
    
    List lTreeNodeIds = this.getTreeNodeIds(lTreeNodes);
    //search and remove node ids from cache
    for (int i=(lTreeNodeIds.size()-1); i>=0; i--) {
      if (this.permissionManager.containsPermissionKey(OBJECT_TYPE+""+lTreeNodeIds.get(i))) {
        lTreeNodeIds.remove(i);
      }
    }
    //if some nodes has no permission cache then add the permission from database
    if (lTreeNodeIds.size()>0) {
      Integer userRecordId =  this.sessionContainer.getUserRecordID();
      
      Map mResultPermission = new HashMap();
      if (DmsRoot.PERSONAL_ROOT.equals(rootType) || ownerId.equals(userRecordId)) {//Update By Jim Zhou      2006/12/07      EIP-777
        String permission = this.permissionManager.getPersonalPermission();
        for (int i=0; i<lTreeNodeIds.size(); i++ ) {
          mResultPermission.put(OBJECT_TYPE+""+lTreeNodeIds.get(i), permission);
        }
      }else {
        List noCacheIds = new ArrayList(lTreeNodeIds);
        List lUserRoleIds = this.getUserRoleIds(this.permissionManager.getUserRoles());
        List lUserGroupIds = this.getUserGroupIds(this.permissionManager.getUserGroups());
        
        //step one: put forbidden permission to cache and reomve from list
        Map mForbidPermission = dmsTreeDAObject.getForbiddenPermissionMapWhichNotInMatchRequired(OBJECT_TYPE, lTreeNodeIds, lUserRoleIds, lUserGroupIds, userRecordId);
        if (mForbidPermission.size()>0) {
          Iterator iterator = mForbidPermission.keySet().iterator();
          while (iterator.hasNext()) {
            lTreeNodeIds.remove(new Integer(((String)iterator.next()).substring(1)));
          }
          mResultPermission.putAll(mForbidPermission);
        }
        
        //step two: get setting permission from database(if inherit is true then combine the permission with parent's)
        if (lTreeNodeIds.size()>0) {
          Map mPermission = dmsTreeDAObject.getDirectPermissionMap(OBJECT_TYPE, lTreeNodeIds, lUserRoleIds, lUserGroupIds, userRecordId);
          List notInheritPermissionObjectIds = dmsTreeDAObject.getNotInheritPermissionObjectIds(OBJECT_TYPE, lTreeNodeIds);
          Map  mTreeNodeIds = this.getTreeNodeIdMap(lTreeNodes);
          for (int i=0; i<lTreeNodeIds.size(); i++) {
            Integer objectId = (Integer)lTreeNodeIds.get(i);
            Integer parentId = (Integer)mTreeNodeIds.get(objectId);
            String permission = (String)mPermission.get(OBJECT_TYPE+""+objectId);
            if (parentId.intValue()>0 && !notInheritPermissionObjectIds.contains(objectId)) {
              permission = PermissionManager.combinePermission(permission, (this.permissionManager.getPermission(this.dbConn, OBJECT_TYPE, parentId, rootType, ownerId)));
            }
            mPermission.put(OBJECT_TYPE+""+objectId, permission!=null ? permission : "");
          }
          
          mResultPermission.putAll(mPermission);
        }
        //step three: if user is doc owner then append permission "S"
        if (ownerId.equals(userRecordId)) {
          for (int i=0; i<noCacheIds.size(); i++) {
            Integer objectId = (Integer)noCacheIds.get(i);
            String permission = (String)mResultPermission.get(OBJECT_TYPE+""+objectId);
            mResultPermission.put(OBJECT_TYPE+""+objectId, this.forceToAddPermission(permission, "S"));
          }
        }
      }
      
      this.permissionManager.putAllPermission(mResultPermission);
    }
  }
  
  private String forceToAddPermission(String permission, String accessType) throws Exception {
    accessType = accessType.toUpperCase();
    if (permission.indexOf(accessType)!=-1) {
      permission.replace(accessType.toLowerCase().charAt(0), accessType.charAt(0));
    }else {
      permission += accessType;
    }
    return permission;
  }
  
  private List getTreeNodeIds(List lTreeNodes) throws Exception {
    List lTreeNodeIds = new ArrayList();
    for (int i=0; i<lTreeNodes.size(); i++) {
      lTreeNodeIds.add(((DmsTreeNode)lTreeNodes.get(i)).getId());
    }
    return lTreeNodeIds;
  }
  
  private Map getTreeNodeIdMap(List lTreeNodes) throws Exception {
    Map mTreeNodeIds = new HashMap();
    for (int i=0; i<lTreeNodes.size(); i++) {
      DmsTreeNode node = (DmsTreeNode)lTreeNodes.get(i);
      mTreeNodeIds.put(node.getId(), node.getParentId());
    }
    return mTreeNodeIds;
  }
  
  private List getFilterTreeNodes(List lTreeNodes) throws Exception {
    for (int i=(lTreeNodes.size()-1); i>=0; i--) {
      Integer nodeId = ((DmsTreeNode)lTreeNodes.get(i)).getId();
      if (!this.permissionManager.hasAccessRight(this.dbConn, OBJECT_TYPE, nodeId, null, null, FILTER_RIGHT)) {
        lTreeNodes.remove(i);
      }
    }
    return lTreeNodes;
  }

  private List getTreeNodesAfterSetHasChildren(List lTreeNodes, List lChildrenTreeNodes) {
    List lResult = new ArrayList();
    List lChildrenPIds = new ArrayList();
    if (lChildrenTreeNodes!=null && lChildrenTreeNodes.size()>0) {
      for (int i=0; i<lChildrenTreeNodes.size(); i++) {
        lChildrenPIds.add(((DmsTreeNode)lChildrenTreeNodes.get(i)).getParentId());
      }
    }
    for (int i=0; i<lTreeNodes.size(); i++) {
      DmsTreeNode node = (DmsTreeNode)lTreeNodes.get(i);
      node.setHasChildren(lChildrenPIds.contains(node.getId()));
      lResult.add(node);
    }
    return lResult;
  }
  
  private List getUserRoleIds(List userRoles) throws Exception {
    List userRoleIds = new ArrayList();
    for (int i=0; i<userRoles.size(); i++) {
      UserRole userRole = (UserRole)userRoles.get(i);
      userRoleIds.add(userRole.getID());
    }
    return userRoleIds;
  }
  
  private List getUserGroupIds(List userGroups) throws Exception {
    List userGroupIds = new ArrayList();
    for (int i=0; i<userGroups.size(); i++) {
      UserGroup userGroup = (UserGroup)userGroups.get(i);
      userGroupIds.add(userGroup.getID());
    }
    return userGroupIds;
  }
  
}